/*
 * @Author: 李琪 liqi@lark2game.com
 * @Date: 2024-12-07 10:57:20
 * @LastEditors: 李琪 liqi@lark2game.com
 * @LastEditTime: 2024-12-07 15:39:23
 * @FilePath: /cramped-room/assets/Script/Runtime/DataManager.ts
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
import { resources, SpriteFrame, Prefab, AudioClip, SpriteAtlas, ParticleAsset, JsonAsset, TextAsset, Font, Asset, sp, AssetManager, assetManager } from "cc";
import Singleton from "../Base/Singleton";
import { Browser } from "../Base/Browser";

export default class ResourceManager extends Singleton {
    /** 通用接口 */
    private resourceData: any;
    // static get Instance() {
    //     return super.getInstance<ResourceManager>();
    // }
    private static instance: ResourceManager;


    public static get Instance(): ResourceManager {
        if (!ResourceManager.instance) {
            ResourceManager.instance = new ResourceManager();
        }
        return ResourceManager.instance;
    }

    public async init() {
        /** 重试下载的最大次数 */
        assetManager.downloader.maxRetryCount = 3;
        /** 重试下载的间隔时间(毫秒) */
        assetManager.downloader.retryInterval = 2000;
        let data: JsonAsset
        if (Browser.onMiniGame) {
            if (Browser.onHWMiniGame || Browser.onOppoMiniGame) {
                console.log('此处单独处理华为小游戏加载资源路径');
                data = await this.loadResAsync('ResourceList', JsonAsset);
            } else {
                data = await this.loadBundleResAsync('main', 'ResourceList', JsonAsset);
            }
        } else {
            data = await this.loadResAsync('ResourceList', JsonAsset);
        }

        this.resourceData = data.json;
    }
    /*######################## resource 资源加载 #######################*/
    /** 只能获取 resource 文件夹下的资源 */
    public getRes(path: string, type: typeof Asset): any {
        return resources.get(path, type);
    }

    /** 加载预制体 */
    public loadResPrefab(path: string, onComplete?: Function) {
        this.loadRes(path, Prefab, onComplete);
    }

    /** 异步加载预制体 */
    public async loadResPrefabAsync(path: string): Promise<Prefab> {
        let file: Prefab = this.getRes(path, Prefab);
        if (!file) {
            file = await this.loadResAsync(path, Prefab);
        }
        return file;
    }

    /** 加载散图 */
    public loadResImage(path: string, onComplete?: Function) {
        this.loadRes(path, SpriteFrame, onComplete);
    }

    /** 异步加载散图 */
    public async loadResImageAsync(path: string): Promise<SpriteFrame> {
        const file: SpriteFrame = await this.loadResAsync(path, SpriteFrame);
        return file;
    }
    /** 加载prefab */
    public loadPrefab(name: string | string[], onComplete?: Function, onFail?: Function) {
        this.load(name, Prefab, onComplete, onFail);
    }

    public async loadPrefabAsync(name: string | string[]): Promise<any> {
        return await this.loadAsync(name, Prefab);
    }

    /** 加载particle */
    public loadParticle(name: string | string[], onComplete?: Function) {
        this.load(name, ParticleAsset, onComplete);
    }

    public async loadParticleAsync(name: string | string[]): Promise<ParticleAsset> {
        return await this.loadAsync(name, ParticleAsset);
    }

    /** 加载spine */
    public loadSpine(name: string | string[], onComplete?: Function, onFail?: Function) {
        this.load(name, sp.SkeletonData, onComplete, onFail);
    }

    public async loadSpineAsync(name: string | string[]): Promise<sp.SkeletonData> {
        return await this.loadAsync(name, sp.SkeletonData);
    }

    /** 加载resource图集 */
    public loadResAtlas(path: string, onComplete?: Function) {
        this.loadRes(path, SpriteAtlas, onComplete);
    }

    /** 异步加载resource图集 */
    public async loadResAtlasAsync(path: string): Promise<SpriteAtlas> {
        const file: SpriteAtlas = await this.loadResAsync(path, SpriteAtlas);
        return file;
    }
    /** 加载boundle图集 */
    public loadAtlas(name: string | string[], onComplete?: Function, onFail?: Function) {
        this.load(name, SpriteAtlas, onComplete, onFail);
    }

    public async loadAtlasAsync(name: string | string[]): Promise<SpriteAtlas> {
        return await this.loadAsync(name, SpriteAtlas);
    }

    /** 加载json */
    public loadResJson(path: string, onComplete?: Function) {
        this.loadRes(path, JsonAsset, onComplete);
    }

    /** 异步加载json */
    public async loadResJsonAsync(path: string): Promise<JsonAsset> {
        const file: JsonAsset = await this.loadResAsync(path, JsonAsset);
        return file;
    }

    /** 加载音效 */
    public loadResAudio(path: string, onComplete?: Function) {
        this.loadRes(path, AudioClip, onComplete);
    }

    /** 异步加载音效 */
    public async loadResAudioAsync(path: string): Promise<AudioClip> {
        const file: AudioClip = await this.loadResAsync(path, AudioClip);
        return file;
    }

    public async loadDirAsync(path: string, type: typeof SpriteFrame = SpriteFrame): Promise<SpriteFrame[]> {
        return new Promise<SpriteFrame[]>((resolve, reject) => {
            //加载资源
            resources.loadDir(path, type, function (err, res) {
                if (err) {
                    reject(err);
                    return;
                }
                resolve(res);
            })
        })
    }
    public async loadAsync(name: string | string[], type: typeof Asset): Promise<any> {
        if (Array.isArray(name)) {
            const paramArr = [];
            for (const path of name) {
                const param = this.getParam(path, type);
                if (param) {
                    paramArr.push(param);
                }
            }
            const resArr = [];
            for (const param of paramArr) {
                let res = null;
                if (param.bundle != '') {
                    res = await this.loadBundleResAsync(param.bundle, param.path, type);
                }
                else {
                    res = await this.loadResAsync(param.path, type);
                }
                if (res) {
                    resArr.push(res);
                }
            }
            return resArr;
        }

        const param = this.getParam(name, type);
        if (param) {
            if (param.bundle && param.bundle != '') {
                return await this.loadBundleResAsync(param.bundle, param.path, type);
            }

            return await this.loadResAsync(param.path, type);
        }

        console.error('error load', name);
        return null;
    }
    /** 异步加载resources中的资源 */
    public loadResAsync(path: string, type: typeof Asset): Promise<any> {
        if (path == '') {
            return null;
        }
        return new Promise((resolve, reject) => {
            resources.load(path, type, (error: Error, resource: any) => {
                if (error) {
                    reject(error);
                } else {
                    console.log(`加载资源-->from res async--> ${path}`);
                    resolve(resource);
                }
            });
        });
    }
    /** 异步加载bundle中资源 */
    public loadBundleResAsync(name: string, url: string, type: typeof Asset): Promise<any> {
        return new Promise(async (resolve, reject) => {
            let bundle: AssetManager.Bundle = this.getBundle(name);
            if (!bundle) {
                bundle = await this.loadBundleAsync(name).catch((error) => {
                    reject(error);
                });
            }

            if (bundle) {
                bundle.load(url, type, (error: Error, assets: any) => {
                    if (error) {
                        reject(error);
                    }
                    else {
                        console.log(`加载资源-->from bundle async--> ${name}`);
                        resolve(assets);
                    }
                });
            }
        });
    }
    /** 异步加载bundle */
    public loadBundleAsync(name: string): Promise<any> {
        return new Promise((resolve, reject) => {
            assetManager.loadBundle(name, (err: Error, bundle: AssetManager.Bundle) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve(bundle);
                }
            });
        });
    }
    /**
    * 获取加载过缓存下来的bundle资源
    * @param name
    */
    public getBundle(name: string): AssetManager.Bundle {
        return assetManager.getBundle(name);
    }

    /**
     * 移出bundle
     * @param bundle
     * @param release 是否释放所有资源
     */
    public removeBundle(bundle: AssetManager.Bundle, release = false) {
        if (bundle) {
            if (release) {
                this.releaseBundleAllRes(bundle.name);
                bundle.releaseAll();
            }
            assetManager.removeBundle(bundle);
        }
    }
    /**
    * 释放某个bundle所有资源
    * @param name
    */
    public releaseBundleAllRes(name: string) {
        const bundle = this.getBundle(name);
        if (bundle) {
            bundle.releaseAll();
        }
    }
    public load(name: string | string[], type: typeof Asset, onComplete?: Function, onFail?: Function, onProgressCall?: Function) {
        if (Array.isArray(name)) {
            const paramArr = [];
            for (const path of name) {
                const param = this.getParam(path, type);
                if (param) {
                    paramArr.push(param);
                }
            }
            const total = paramArr.length;
            let count = 0;
            const resArr = [];
            const completeCall = (res) => {
                count++;
                resArr.push(res);
                if (onProgressCall) {
                    onProgressCall(count, count / total, res);
                }
                if (count >= total) {
                    onComplete && onComplete(resArr);
                }
            };
            if (paramArr.length > 0) {
                for (const param of paramArr) {
                    if (param.bundle != '') {
                        this.loadBundleRes(param.bundle, param.path, type, completeCall, onFail);
                    }
                    else {
                        this.loadRes(param.path, type, completeCall, onFail);
                    }
                }
            }
            else {
                onComplete && onComplete(resArr);
            }
        }
        else {
            const param = this.getParam(name, type);
            if (param) {
                if (param.bundle && param.bundle != '') {
                    this.loadBundleRes(param.bundle, param.path, type, onComplete, onFail);
                }
                else {
                    this.loadRes(param.path, type, onComplete, onFail);
                }
            }
            else {
                console.error('error load', name);
            }
        }
    }
    /** 加载resources中的资源，有回调 */
    public loadRes(path: string, type: typeof Asset, onComplete?: Function, onFail?: Function) {
        if (path?.length > 0) {
            resources.load(path, type, (error: Error, assets: any) => {
                if (error) {
                    console.error('loadRes error', path, error);
                    if (onFail) {
                        onFail();
                    }
                }
                else {
                    console.log(`加载资源-->from res--> ${path}`);

                    if (onComplete) {
                        onComplete(assets);
                    }
                }
            });
        }
        else {
            if (onFail) {
                onFail();
            }
        }
    }
    /**
 * 加载bundle中的资源
 * @param name bundle名
 * @param url 资源路径
 * @param type 资源类型
 * @param loadCompleted
 */
    public loadBundleRes(name: string, url: string, type: typeof Asset, onComplete?: Function, onFail?: Function) {
        const completeCallback = (bundle: AssetManager.Bundle) => {
            // DYTools.log("loadBundleRes " + url);
            bundle.load(url, type, (error: Error, assets: any) => {
                if (error) {
                    console.log(`load bundle res error--> ${url}`);
                    onFail && onFail(error);
                }
                else {
                    console.log(`加载资源-->from bundle--> ${url}`);
                    onComplete && onComplete(assets);
                }
            });
        };
        const bundle: AssetManager.Bundle = this.getBundle(name);
        if (bundle) {
            completeCallback(bundle);
        }
        else {
            this.loadBundle(name, completeCallback);
        }
    }
    /**
* 加载bundle
* @param name 地址或名称
* @param onCompleted 加载完成回调
*/
    public loadBundle(name: string, onCompleted?: Function, onFail?: Function) {
        assetManager.loadBundle(name, (err: Error, bundle: AssetManager.Bundle) => {
            if (err) {
                onFail && onFail(err);
            }
            else {
                onCompleted && onCompleted(bundle);
            }
        });
    }
    public getParam(name: string, type: typeof Asset) {
        let ret = { bundle: '', path: '' };
        const param = this.getUrl(name, type);
        let isNull = false;
        if (param) {
            if (param.path && param.path != '') {
                ret.path = param.path;
            }
            else {
                isNull = true;
            }
            if (!isNull) {
                if (param.bundle && param.bundle != '') {
                    ret.bundle = param.bundle;
                }
            }
        }
        if (isNull) {
            ret = null;
        }
        return ret;
    }
    public getUrl(name: string, type: typeof Asset, log = true): { bundle: string, path: string } {
        let typeName = '';
        switch (type) {
            case Prefab:
                typeName = 'prefab';
                break;
            case SpriteFrame:
                typeName = 'texture';
                break;
            case AudioClip:
                typeName = 'audio';
                break;
            // case sp.SkeletonData:
            //     typeName = 'spine';
            //     break;
            case SpriteAtlas:
                typeName = 'texture';
                break;
            case ParticleAsset:
                typeName = 'particle';
                break;
            case JsonAsset:
                typeName = 'config';
                break;
            case TextAsset: {
                typeName = 'config';
                break;
            }
            case Font: {
                typeName = 'font';
                break;
            }
        }

        let url = null;
        if (this.resourceData[typeName]) {
            url = this.resourceData[typeName][name];
        }
        if (url == null) {
            if (type == SpriteAtlas) {
                typeName = 'texture';
                if (this.resourceData[typeName]) {
                    url = this.resourceData[typeName][name];
                }
            }
            if (!url && log) {
                console.warn(`not find res ${name}`);
            }
        }
        return url;
    }


}
